package ro.flashbite.loggers
{
	import flash.utils.Dictionary;
	
	import ro.flashbite.errors.DontInstanciateError;
	import ro.flashbite.errors.NotInitializedError;
	import ro.flashbite.interfaces.IDestroyable;
	import ro.flashbite.loggers.targets.ILoggingTarget;
	import ro.flashbite.loggers.ILogger;
	
	/**
	 * Factory implementation for Loggers;
	 * 
	 * Usage : 
	 * 		step 1:  call initialize();
	 * 		step 2a: add a target with addTarget;
	 * 		step 2b: remove a target with removeTarget;
	 * 		step 3a: when you need a logger, just get it with getLogger(yourLoggerName);
	 * 		step 3b: when you don't need a logger, just remove it with clearLogger(yourLoggerName);
	 * 		step 4:  when you need to filter messages, just set a new maxOutputLevel;
	 * 		step 5:  when you don't need anymore any functionality of this class, just call destroy()
	 *  
	 * v 1.0
	 * 
	 * @author Adrian Barbu
	 */	
	public final class LoggerFactory
	{
		/**
		 * holds all loggers by name 
		 */		
		private static var _loggerDatabase		:	Dictionary;
		/**
		 * holds all logging targets 
		 */		
		private static var _outputTargetDatabase:	Vector.<ILoggingTarget>;
		/**
		 * the max level on wich each logger can send messages 
		 */		
		private static var _maxOutputLevel		:	uint;
		/**
		 * tells if this factory is initialized and can do his work 
		 */		
		private static var _initialized			:	Boolean = false;
		
		public function LoggerFactory() { throw new DontInstanciateError(); }
		
		// ================================================================================================================================
		// PUBLIC
		// ================================================================================================================================
		
		// --------------------------------------------------------------------------------------------------------------------------------
		// initializing
		/**
		 * Initialize the class;
		 * 
		 * @param maxOutputLevel : (uint) the max level on wich each logger can send messages 
		 */		
		public static function initialize(maxOutputLevel:uint):void
		{
			_initialized = true;
			
			_loggerDatabase = new Dictionary(true);
			_outputTargetDatabase = new Vector.<ILoggingTarget>();
			
			setMaxOutputLevel(maxOutputLevel);
		}
		// --------------------------------------------------------------------------------------------------------------------------------
		
		// --------------------------------------------------------------------------------------------------------------------------------
		// setter for global max output level
		/**
		 * Sets the new max output level internally and to all registered loggers
		 * 
		 * @param v : (uint) the new max output level to use  
		 */		
		public static function setMaxOutputLevel(v:uint):void 
		{ 
			if (_initialized == false) throw new NotInitializedError();
			
			_maxOutputLevel = Math.min(v, LoggerLevels.ALL); 
			
			//set also on loggers the max output level
			var loggerName:String,
				logger:Logger;
			for (loggerName in _loggerDatabase) {
				logger = _loggerDatabase[loggerName];
				logger.maxOutputLevel = _maxOutputLevel;
			}
		}
		// --------------------------------------------------------------------------------------------------------------------------------
		
		// --------------------------------------------------------------------------------------------------------------------------------
		// logger's management
		/**
		 * Creates a new logger if the name is unique, else just return a already created/registered logger 
		 * 
		 * @param name : (String) the name of the logger
		 * @return : (ILogger) logger with name for sending messages
		 */		
		public static function getLogger(name:String):ILogger
		{
			if (_initialized == false) throw new NotInitializedError();
			
			var logger:Logger = _loggerDatabase[name];
			if (logger == null) {
				//create logger
				logger = new Logger(name, _maxOutputLevel);
				//add internal outputs
				if (_outputTargetDatabase.length) {
					for each (var iLogTarget:ILoggingTarget in _outputTargetDatabase)
						logger.addOutputTarget(iLogTarget);
				}
				//save in database
				_loggerDatabase[name] = logger;
			}
			
			return logger;
		}
		/**
		 * Removes a logger from database
		 * 
		 * @param name : (String) the name of the logger to remove
		 * @return : (Boolean) true if the logger was successfully removed, false otherwise
		 */		
		public static function clearLogger(name:String):Boolean
		{
			if (_initialized == false) throw new NotInitializedError();
			
			var logger:Logger = _loggerDatabase[name];
			if (logger) {
				//clean-up
				logger.destroy();
				_loggerDatabase[name] = null;
				delete _loggerDatabase[name];
				
				return true;
			}
			
			return false;
		}
		// --------------------------------------------------------------------------------------------------------------------------------
		
		// --------------------------------------------------------------------------------------------------------------------------------
		// target's management
		
		/**
		 * Adds a output target to internal database and to each registered logger 
		 * 
		 * @param logTarget : (ILoggingTarget) the target to add
		 */		
		public static function addTarget(logTarget:ILoggingTarget):void
		{
			if (_initialized == false) throw new NotInitializedError();
			
			if (getLogTargetPosition(logTarget) == -1) {
				//save in database
				_outputTargetDatabase.push(logTarget);
				//update each logger database
				var loggerName:String,
					logger:Logger;
				for (loggerName in _loggerDatabase) {
					logger = _loggerDatabase[loggerName];
					logger.addOutputTarget(logTarget);
				}
			} else trace("The same logTarget is already registered in database");
		}
		
		/**
		 * Remove a output target from internal database and from each registered logger 
		 * 
		 * @param logTarget : (ILoggingTarget) the target to add
		 */	
		public static function removeTarget(logTarget:ILoggingTarget):void
		{
			if (_initialized == false) throw new NotInitializedError();
			
			var position:int = getLogTargetPosition(logTarget);
			if (position != -1) {
				//remove from database
				_outputTargetDatabase.splice(position, 1);
				//remove from each logger database
				var loggerName:String,
					logger:Logger;
				for (loggerName in _loggerDatabase) {
					logger = _loggerDatabase[loggerName];
					logger.removeOutputTarget(logTarget);
				}
			} else trace("This logTarget isn't registered in database");
		}
		// --------------------------------------------------------------------------------------------------------------------------------
		
		// ================================================================================================================================
		// PRIVATE
		// ================================================================================================================================
		
		/**
		 * Search for a target in database and return the index of it
		 * 
		 * @param target : (ILoggingTarget) the target to search
		 * @return : (int) position in vector
		 */		
		private static function getLogTargetPosition(target:ILoggingTarget):int { return _outputTargetDatabase.indexOf(target); }
		
		// ================================================================================================================================
		// DESTRUCTOR
		// ================================================================================================================================
		
		/** 
		 * Proper destroy
		 */		
		public static function destroy():void
		{
			_initialized = false;
			
			//clear loggers
			if (_loggerDatabase) {
				var loggerName:String,
					logger:IDestroyable;
				for (loggerName in _loggerDatabase) {
					logger = _loggerDatabase[loggerName] as Logger;
					if (logger) 
						logger.destroy();
					_loggerDatabase[loggerName] = null;
					delete _loggerDatabase[loggerName];
				}
				_loggerDatabase = null;
			}
			
			//clear output targets
			if (_outputTargetDatabase) {
				if (_outputTargetDatabase.length) {
					for each (var output:IDestroyable in _outputTargetDatabase)
						output.destroy();
				}
				_outputTargetDatabase = null;
			}
		}
	}
}